package api

import (
	"math/big"
	"kumachan/standalone/ctn"
	"kumachan/standalone/util"
	"kumachan/interpreter/runtime/rt"
)


func __SeqReverse(s rt.Seq) rt.Seq {
	return rt.ListFrom(__SeqToList(s)).Reversed().Iterate()
}

func __SeqCount(n *big.Int) rt.Seq {
	return rt.IntervalSeq {
		Current: big.NewInt(0),
		Bound:   n,
	}
}
func __SeqInclusiveRange(l *big.Int, r *big.Int) rt.Seq {
	if r.Cmp(l) < 0 {
		panic("invalid sequence: lower bound bigger than upper bound")
	}
	return rt.IntervalSeq {
		Current: l,
		Bound:   big.NewInt(0).Add(r, big.NewInt(1)),
	}
}

func __SeqShift(seq rt.Seq) rt.EnumValue {
	var item, rest, exists = seq.Next()
	if exists {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}
func __SeqNil() rt.Seq {
	return rt.EmptySeq { ItemType: rt.ValueReflectType() }
}
func __SeqCons(head rt.Value, tail rt.Seq) rt.Seq {
	return rt.ConsSeq {
		Head: head,
		Tail: tail,
	}
}

func __SeqToList(seq rt.Seq) rt.Value {
	return rt.SeqCollect(seq)
}
func __SeqWithIndex(input rt.Seq) rt.Value {
	return rt.MappedSeq {
		Input: rt.IndexedSeq {
			Data:      input,
			NextIndex: 0,
		},
		Mapper: func(v rt.Value) rt.Value {
			var tuple = v.(rt.TupleValue)
			var index = &(tuple.Elements[1])
			*index = util.GetNumberUint((*index).(uint))
			return tuple
		},
	}
}

func __SeqMap(input rt.Seq, f rt.Value, h rt.InteropContext) rt.Seq {
	return rt.MappedSeq {
		Input:  input,
		Mapper: func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
	}
}
func __SeqDeflateMap(input rt.Seq, f rt.Value, h rt.InteropContext) rt.Seq {
	return rt.DeflateMappedSeq {
		Input:      input,
		MapFilter: func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
	}
}
func __SeqFilter(input rt.Seq, f rt.Value, h rt.InteropContext) rt.Seq {
	return rt.FilteredSeq {
		Input:  input,
		Filter: func(item rt.Value) bool {
			return rt.FromBool(h.Call(f, item).(rt.EnumValue))
		},
	}
}
func __SeqFlatMap(input rt.Seq, f rt.Value, h rt.InteropContext) rt.Seq {
	return rt.FlatMappedSeq {
		Input:   input,
		Mapper:  func(item rt.Value) rt.Value {
			return h.Call(f, item)
		},
		Current: rt.EmptySeq { ItemType: input.GetItemType() },
	}
}

func __SeqChunk(input rt.Seq, size *big.Int) rt.Value {
	return rt.ChunkedSeq {
		ChunkSize: util.GetUintNumber(size),
		Remaining: input,
	}
}
func __SeqConcat(a rt.Seq, b rt.Seq) rt.Value {
	return rt.ConcatenatedSeq {
		Remaining: a,
		Another:   b,
	}
}

func __SeqScan(input rt.Seq, init rt.Value, f rt.Value, h rt.InteropContext) rt.Seq {
	return rt.ScannedSeq {
		Previous: init,
		Rest:     input,
		Reducer: func(prev rt.Value, cur rt.Value) rt.Value {
			return h.Call(f, rt.Tuple(prev, cur))
		},
	}
}
func __SeqReduce(input rt.Seq, init rt.Value, f rt.Value, h rt.InteropContext) rt.Value {
	return rt.SeqReduce(input, init, func(prev rt.Value, cur rt.Value) rt.Value {
		return h.Call(f, rt.Tuple(prev, cur))
	})
}
func __SeqSome(input rt.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	return rt.ToBool(rt.SeqSome(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	}))
}
func __SeqEvery(input rt.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	return rt.ToBool(rt.SeqEvery(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	}))
}

func __SeqFindFirst(input rt.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	var value, found = rt.SeqGetFirst(input, func(item rt.Value) bool {
		return rt.FromBool(h.Call(f, item).(rt.EnumValue))
	})
	if found {
		return rt.Some(value)
	} else {
		return rt.None()
	}
}
func __SeqGetFirst(input rt.Seq, f rt.Value, h rt.InteropContext) rt.EnumValue {
	var value, found = rt.SeqMapFirst(input, func(item rt.Value) (rt.Value, bool) {
		return rt.Unwrap(h.Call(f, item).(rt.EnumValue))
	})
	if found {
		return rt.Some(value)
	} else {
		return rt.None()
	}
}

func __SeqZip(tuple rt.TupleValue) rt.Seq {
	var seq_list = make([] rt.Seq, len(tuple.Elements))
	for i, v := range tuple.Elements {
		seq_list[i] = v.(rt.Seq)
	}
	return rt.MappedSeq {
		Input: rt.ZippedSeq { SeqList: seq_list },
		Mapper: func(v rt.Value) rt.Value {
			return rt.TupleOf(v.([]rt.Value))
		},
	}
}
func __SeqZipLongest(tuple rt.TupleValue) rt.Seq {
	var seq_list = make([] rt.Seq, len(tuple.Elements))
	for i, v := range tuple.Elements {
		seq_list[i] = v.(rt.Seq)
	}
	return rt.MappedSeq {
		Input: rt.ZippedLongestSeq { SeqList: seq_list },
		Mapper: func(v rt.Value) rt.Value {
			return rt.TupleOf(v.([]rt.Value))
		},
	}
}
func __SeqZipList(v rt.Value) rt.Seq {
	var l = rt.ListFrom(v)
	var seq_list = make([]rt.Seq, l.Length())
	l.ForEach(func(i uint, v rt.Value) {
		seq_list[i] = v.(rt.Seq)
	})
	return rt.ZippedSeq { SeqList: seq_list }
}
func __SeqZipLongestList(v rt.Value) rt.Seq {
	var l = rt.ListFrom(v)
	var seq_list = make([]rt.Seq, l.Length())
	l.ForEach(func(i uint, v rt.Value) {
		seq_list[i] = v.(rt.Seq)
	})
	return rt.ZippedLongestSeq { SeqList: seq_list }
}

func __ListToSeq(v rt.Value) rt.Seq {
	return rt.ListFrom(v).Iterate()
}

func __ListLength(v rt.Value) *big.Int {
	var arr = rt.ListFrom(v)
	return util.GetNumberUint(arr.Length())
}

func __ListEmpty(v rt.Value) rt.EnumValue {
	var arr = rt.ListFrom(v)
	return rt.ToBool(arr.Length() == 0)
}

func __ListReverse(v rt.Value) rt.Value {
	var arr = rt.ListFrom(v)
	return arr.Reversed()
}
func __ListShift(v rt.Value) rt.EnumValue {
	var item, rest, ok = rt.ListFrom(v).Shifted()
	if ok {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}
func __ListPop(v rt.Value) rt.EnumValue {
	var item, rest, ok = rt.ListFrom(v).Popped()
	if ok {
		return rt.Some(rt.Tuple(item, rest))
	} else {
		return rt.None()
	}
}

func __Set(values_ rt.Value, ctx ([] rt.Value), h rt.InteropContext) rt.Set {
	var values = rt.ListFrom(values_)
	var cmp = rt.Compare(func(a rt.Value, b rt.Value) ctn.Ordering {
		var t = h.Call(ctx[0], rt.Tuple(a, b))
		return rt.FromOrdering(t.(rt.EnumValue))
	})
	var s = rt.NewSet(cmp)
	values.ForEach(func(i uint, item rt.Value) {
		s = s.Inserted(item)
	})
	return s
}
func __SetValues(s rt.Set) ([] rt.Value) {
	return s.Items()
}
func __SetHas(set rt.Set, v rt.Value) rt.EnumValue {
	return rt.ToBool(set.Has(v))
}

func __Map(entries_ rt.Value, ctx ([] rt.Value), h rt.InteropContext) rt.Map {
	var entries = rt.ListFrom(entries_)
	var cmp = rt.Compare(func(a rt.Value, b rt.Value) ctn.Ordering {
		return rt.FromOrdering(h.Call(ctx[0], rt.Tuple(a, b)).(rt.EnumValue))
	})
	var m = rt.NewMap(cmp)
	entries.ForEach(func(_ uint, item rt.Value) {
		var key, value = rt.PairFromTuple2(item.(rt.TupleValue))
		m = m.Inserted(key, value)
	})
	return m
}
func __MapEntries(m rt.Map) ([] rt.TupleValue) {
	return ctn.MapEach(m.Entries(), func(p ctn.Pair[rt.Value,rt.Value]) rt.TupleValue {
		return rt.Tuple(p.Key(), p.Value())
	})
}
func __MapLookup(m rt.Map, k rt.Value) rt.EnumValue {
	var v, exists = m.Lookup(k)
	if exists {
		return rt.Some(v)
	} else {
		return rt.None()
	}
}
func __MapInsert(m rt.Map, entry_ rt.Value) rt.Map {
	var entry = entry_.(rt.TupleValue)
	var k = entry.Elements[0]
	var v = entry.Elements[1]
	return m.Inserted(k, v)
}
func __MapDelete(m rt.Map, k rt.Value) rt.Map {
	return m.Deleted(k)
}

// TODO: move
func __ProjRef_f(v rt.Value) rt.Value { return v }
// TODO: move
func __CaseRef_f(v rt.Value) rt.Value { return v }


